package cn.iocoder.yudao.module.service.websocket;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.websocket.core.listener.WebSocketMessageListener;
import cn.iocoder.yudao.framework.websocket.core.sender.WebSocketMessageSender;
import cn.iocoder.yudao.framework.websocket.core.util.WebSocketFrameworkUtils;
import cn.iocoder.yudao.module.service.constant.websocket.WebSocketConstant;
import cn.iocoder.yudao.module.service.controller.admin.stepconfig.vo.StepConfigPageReqVO;
import cn.iocoder.yudao.module.service.controller.admin.stepconfig.vo.StepConfigRespVO;
import cn.iocoder.yudao.module.service.dal.dataobject.assistant.ServiceAssistantDO;
import cn.iocoder.yudao.module.service.dal.dataobject.stepconfig.StepConfigDO;
import cn.iocoder.yudao.module.service.dal.dataobject.stepform.StepFormDO;
import cn.iocoder.yudao.module.service.dal.dataobject.welcomeconfig.ServiceWelcomeConfigDO;
import cn.iocoder.yudao.module.service.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.service.enums.websocket.MessageTypeEnum;
import cn.iocoder.yudao.module.service.mq.producer.chat.ServiceChatProducer;
import cn.iocoder.yudao.module.service.service.assistant.ServiceAssistantService;
import cn.iocoder.yudao.module.service.service.stepconfig.StepConfigService;
import cn.iocoder.yudao.module.service.service.stepform.StepFormService;
import cn.iocoder.yudao.module.service.service.stepformdata.StepFormDataService;
import cn.iocoder.yudao.module.service.service.welcomeconfig.ServiceWelcomeConfigService;
import cn.iocoder.yudao.module.service.websocket.message.ServiceChatReceiveMessage;
import cn.iocoder.yudao.module.service.websocket.message.ServiceChatSendMessage;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * 客服系统WebSocket消息监听器
 * 1. 监听客户上线 -> 查询或创建客户对象 -> 匹配客服 -> 获取欢迎语、场景配置 -> 自动执行消息发送
 * 2. 监听客户离线 -> 清除客服/客户缓存关系
 * 3. 监听客户消息 -> 创建对话记录
 * 4. 监听客户标签消息 -> 查询标签 -> 返回标签答复内容 -> 返回下一级标签列表
 * 5. 监听客户表单消息 -> 创建表单记录
 * -----
 *
 *
 * @author 芋道源码
 */
@Component
@Slf4j
public class ServiceWebSocketMessageListener implements WebSocketMessageListener<ServiceChatSendMessage> {

    @Resource
    private WebSocketMessageSender webSocketMessageSender;

    @Resource
    private ServiceAssistantService serviceAssistantService;

    @Resource
    private ServiceWelcomeConfigService serviceWelcomeConfigService;

    @Resource
    private StepConfigService stepConfigService;

    @Resource
    private StepFormService stepFormService;

    @Resource
    private StepFormDataService stepFormDataService;

    @Resource
    private ServiceChatProducer serviceChatProducer;

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    private Long tenantId;
    private Integer userType;
    private Integer messageType;
    private String text;
    private Long fromUserId;
    private String fromUserKey;
    private Long toUserId;

    @Override
    public void onMessage(WebSocketSession session, ServiceChatSendMessage message) {
        LoginUser loginUser = WebSocketFrameworkUtils.getLoginUser(session);
        // 租户
        setTenantId(loginUser.getTenantId());
        // 用户类型
        setUserType(loginUser.getUserType());
        // 消息类型
        setMessageType(message.getMessageType());
        // 消息内容
        setText(message.getText());
        // 发送人
        setFromUserId(loginUser.getId());
        setFromUserKey(loginUser.getSessionId());
        // 接收人
        setToUserId(message.getToUserId());

        // 立即发送到消息队列 (排除label类型的，因为需要根据id查询)
        if (ObjectUtils.notEqual(messageType, MessageTypeEnum.LABEL.getValue())) {
            sendServiceChatMessage(tenantId, userType, messageType, fromUserId, toUserId, text);
        }

        // 客服的消息处理
        if (Objects.equals(UserTypeEnum.ADMIN.getValue(), userType)) {
            if (Objects.equals(messageType, MessageTypeEnum.OFFLINE.getValue())) {

                handleServiceOffLineMessage(fromUserId);

            } else if (Objects.equals(messageType, MessageTypeEnum.MESSAGE.getValue())) {

                handleNormalMessage(fromUserId, fromUserKey, toUserId, UserTypeEnum.MEMBER.getValue(), MessageTypeEnum.MESSAGE.getValue(), text);

                // 单方面发送消息，也去更新这个缓存队列
                stringRedisTemplate.opsForSet().add(RedisKeyConstants.SERVICE_CUSTOMER_MAP + fromUserId, String.valueOf(toUserId));
            }
        } else {

            stringRedisTemplate.opsForSet().add(RedisKeyConstants.SERVICE_CUSTOMER_MAP + toUserId, String.valueOf(fromUserId));

            if (Objects.equals(messageType, MessageTypeEnum.ONLINE.getValue())) {

                handleNormalMessage(fromUserId, fromUserKey, toUserId, UserTypeEnum.ADMIN.getValue(), MessageTypeEnum.ONLINE.getValue(), WebSocketConstant.DEFAULT_MSG_CUSTOMER_ONLINE);

                // 查找这个客服，获取欢迎语配置 -> TODO: 优化成缓存查询
                ServiceAssistantDO assistant = serviceAssistantService.getAssistant(toUserId, tenantId);
                if (Objects.isNull(assistant)) {
                    return;
                }
                // 发送欢迎语
                sendWelcomeMessage(assistant.getWelcomeId());
                // 发送场景标签
                sendSceneMessage(toUserId, fromUserId, assistant.getSceneId());
            } else if (Objects.equals(messageType, MessageTypeEnum.MESSAGE.getValue())) {
                // 只能是全词匹配 关键字了
                String autoReply = (String) stringRedisTemplate.opsForHash().get(RedisKeyConstants.AUTO_REPLY_KEY + tenantId, text);
                if (Objects.nonNull(autoReply) && StrUtil.isNotEmpty(autoReply)) {
                    // 立即发送这个回复消息
                    handleNormalMessage(toUserId, null, fromUserId, UserTypeEnum.MEMBER.getValue(), MessageTypeEnum.MESSAGE.getValue(), autoReply);
                } else {
                    handleNormalMessage(fromUserId, fromUserKey, toUserId, UserTypeEnum.ADMIN.getValue(), MessageTypeEnum.MESSAGE.getValue(), text);
                }
            } else if (Objects.equals(messageType, MessageTypeEnum.OFFLINE.getValue())) {
                handleNormalMessage(fromUserId, fromUserKey, toUserId, UserTypeEnum.ADMIN.getValue(), MessageTypeEnum.OFFLINE.getValue(), WebSocketConstant.DEFAULT_MSG_CUSTOMER_OFFLINE);
                // 移除客户和客服的映射关系
                stringRedisTemplate.opsForSet().remove(RedisKeyConstants.SERVICE_CUSTOMER_MAP + toUserId, String.valueOf(fromUserId));
            } else if (Objects.equals(messageType, MessageTypeEnum.LABEL.getValue())) {
                // 客户发送了一个标签， 然后需要返回客户标签答复内容，以及更新一下 下级步骤列表
                try {
                    Long tempId = Long.parseLong(text);
                    handleLabelMessage(tempId);

                } catch (Exception e) {
                    log.error("解析Label消息类型时发生了错误(非数值型ID),原文是：{}", text);
                }
            } else if (Objects.equals(messageType, MessageTypeEnum.FORM.getValue())) {
                handleFormMessage();
            }
        }
    }


    @Override
    public String getType() {
        return WebSocketConstant.MESSAGE_TYPE_SEND;
    }

    /**
     * 发送普通对话消息
     *
     * @param fromUserId  发送人
     * @param fromUserKey 发送人识别码（客户key）
     * @param toUserId    接收人
     * @param text        文本消息
     * @author PetePower
     * @since 2024-04-12
     */
    private void handleNormalMessage(Long fromUserId, String fromUserKey, Long toUserId, Integer userType, Integer messageType, Object text) {
        ServiceChatReceiveMessage toMessage = new ServiceChatReceiveMessage()
                .setFromUserId(fromUserId)
                .setFromUserKey(fromUserKey)
                .setMessageType(messageType)
                .setContent(text)
                .setTimestamp(System.currentTimeMillis());

        webSocketMessageSender.sendObject(userType, toUserId, // 发给客户
                WebSocketConstant.MESSAGE_TYPE_RECEIVE, toMessage);

    }

    /**
     * 处理客服离线消息
     *
     * @param fromUserId 客户id
     * @author PetePower
     * @since 2024-04-12
     */
    private void handleServiceOffLineMessage(Long fromUserId) {
        Set<String> members = stringRedisTemplate.opsForSet().members(RedisKeyConstants.SERVICE_CUSTOMER_MAP + fromUserId);
        if (CollectionUtil.isNotEmpty(members)) {
            ServiceChatReceiveMessage toMessage = new ServiceChatReceiveMessage()
                    .setFromUserId(fromUserId)
                    .setFromUserKey(null)
                    .setMessageType(MessageTypeEnum.OFFLINE.getValue())
                    .setContent(WebSocketConstant.DEFAULT_MSG_SERVICE_OFFLINE);
            members.forEach(customerId -> {
                if (!StrUtil.equals("0", customerId) && !StrUtil.equals("null", customerId) && StrUtil.isNotEmpty(customerId) && ObjUtil.isNotNull(customerId)) {
                    webSocketMessageSender.sendObject(UserTypeEnum.MEMBER.getValue(), Long.parseLong(customerId), // 发给客户
                            WebSocketConstant.MESSAGE_TYPE_RECEIVE, toMessage);
                }
            });
        }
        // 清空关联关系
        stringRedisTemplate.delete(RedisKeyConstants.SERVICE_CUSTOMER_MAP + fromUserId);
    }

    private void handleFormMessage() {
        try {
            // 表单格式数据修改 ：
            // 系统给客户发表单时，发送： {id: 1, formContent: [{formLabel: xxx, formPlaceHolder: xxx}]}
            // 客户填写表单回传时，发送： { formId:1, formContent: [{formLabel: xxx, formValue: xxx}]}
            JSONObject jsonObject = JSONUtil.parseObj(text);
            stepFormDataService.createStepFormData(tenantId, fromUserId, jsonObject.getLong("formId"), jsonObject.getStr("formContent"));
//            JSONArray jsonArray = JSONUtil.parseArray(text);
//            jsonArray.forEach(jsonO -> {
//                JSONObject jsonObject = JSONUtil.parseObj(jsonO);
//
//            });
        } catch (Exception e) {
            log.error("解析FORM类型数据错误, 原文是: {}", text);
        }
    }


    public void sendWelcomeMessage(Long welcomeId) {
        ServiceWelcomeConfigDO welcomeConfig = null;
        if (Objects.nonNull(welcomeId)) {
            welcomeConfig = serviceWelcomeConfigService.getWelcomeConfig(welcomeId, tenantId);
        }
        if (welcomeConfig == null) {
            // 使用一个默认的欢迎语
            welcomeConfig = new ServiceWelcomeConfigDO();
            welcomeConfig.setContent(WebSocketConstant.DEFAULT_MSG_WELCOME_TEXT);
        }
        ServiceChatReceiveMessage toMessage = new ServiceChatReceiveMessage()
                .setFromUserId(toUserId)
                .setFromUserKey(null)
                .setMessageType(MessageTypeEnum.WELCOME.getValue())
                .setContent(welcomeConfig);

        webSocketMessageSender.sendObject(UserTypeEnum.MEMBER.getValue(), fromUserId, // 发给客户
                WebSocketConstant.MESSAGE_TYPE_RECEIVE, toMessage);

        // 创建一个欢迎语记录
//        sendServiceChatMessage(
//                tenantId,
//                UserTypeEnum.ADMIN.getValue(),
//                MessageTypeEnum.WELCOME.getValue(),
//                toUserId,
//                fromUserId,
//                welcomeConfig.getContent()
//        );
    }

    /**
     * 处理场景发送消息 （客户连接进来时，获取客服配置的场景id，然后获取场景下的标签列表，返回给用户）
     *
     * @param fromUserId 客服id
     * @param toUserId   用户id
     * @param sceneId    场景id
     * @author PetePower
     * @since 2024-04-12
     */
    public void sendSceneMessage(Long fromUserId, Long toUserId, Long sceneId) {
        if (Objects.isNull(sceneId)) {
            return;
        }
        StepConfigPageReqVO pageReqVO = new StepConfigPageReqVO();
        pageReqVO.setSceneId(sceneId);
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<StepConfigDO> data = stepConfigService.getStepConfigPage(pageReqVO).getList();
        List<StepConfigRespVO> data2 = BeanUtils.toBean(data, StepConfigRespVO.class);

        handleNormalMessage(fromUserId, null, toUserId, UserTypeEnum.MEMBER.getValue(), MessageTypeEnum.SCENE.getValue(), JSONUtil.toJsonStr(data2));

        // 不需要记录对话记录
    }


    /**
     * 处理标签点击事件
     * 1. 用户点了标签，后台获取点击的标签对象，然后把标签答复内容直接发给用户
     * 2. 获取标签是否配置了下一步，然后把下一步的场景标签发送给用户去更新标签列表
     * 3. 创建相关的对话记录消息
     *
     * @param stepId 标签id
     * @author PetePower
     * @since 2024-04-12
     */
    private void handleLabelMessage(Long stepId) {

        // 发送场景列表
        StepConfigDO stepConfig = stepConfigService.getStepConfig(stepId);
        if (Objects.isNull(stepConfig)) {
            return;
        }

        // 对话记录 - 1 (用户侧点击的标签)
        sendServiceChatMessage(
                tenantId,
                UserTypeEnum.MEMBER.getValue(),
                MessageTypeEnum.LABEL.getValue(),
                fromUserId,
                toUserId,
                stepConfig.getLabel());

        // 双向发送
        handleNormalMessage(fromUserId, fromUserKey, toUserId, UserTypeEnum.ADMIN.getValue(), MessageTypeEnum.MESSAGE.getValue(), stepConfig.getLabel());

        handleNormalMessage(toUserId, null, fromUserId, UserTypeEnum.MEMBER.getValue(), MessageTypeEnum.MESSAGE.getValue(), stepConfig.getContent());

        // 检查： 如果是表单，查找表单的配置项，接着发出去
        if (stepConfig.getType() == 2) {
            StepFormDO stepFormDO = stepFormService.getStepFormByStepId(stepId);
            if (Objects.nonNull(stepFormDO)) {
//                StepFormRespVO respVO = new StepFormRespVO()
//                        .setId(stepFormDO.getId())
//                                .setStepId(stepFormDO.getStepId())
//                                        .setFormContent(stepFormDO.getFormContent());
                JSONObject jsonObject = JSONUtil.parseObj(stepFormDO);
                jsonObject.set("formContent", JSONUtil.parseArray(stepFormDO.getFormContent()));
                handleNormalMessage(toUserId, null, fromUserId, UserTypeEnum.MEMBER.getValue(), MessageTypeEnum.FORM.getValue(), jsonObject);
            }
        }

        // 对话记录 - 2 (系统侧回复标签的内容)
        sendServiceChatMessage(
                tenantId,
                UserTypeEnum.ADMIN.getValue(),
                MessageTypeEnum.LABEL.getValue(),
                toUserId,
                fromUserId,
                stepConfig.getContent());

        // 2. 获取下一级步骤场景
        if (Objects.nonNull(stepConfig.getNextStepId())) {
            sendSceneMessage(toUserId, fromUserId, stepConfig.getNextStepId());
        }
    }


    /**
     * 创建对话记录
     *
     * @param tenantId    租户id
     * @param userType    用户类型
     * @param messageType 消息类型
     * @param fromUserId  发送人
     * @param toUserId    接收人
     * @param text        内容
     * @author PetePower
     * @since 2024-04-12
     */
    private void sendServiceChatMessage(Long tenantId, Integer userType, Integer messageType, Long fromUserId, Long toUserId, String text) {
        serviceChatProducer.sendServiceChatMessage(
                tenantId,
                userType,
                messageType,
                fromUserId,
                toUserId,
                text
        );
    }

    private void setTenantId(Long tenantId) {
        this.tenantId = tenantId;
    }

    private void setUserType(Integer userType) {
        this.userType = userType;
    }

    private void setMessageType(Integer messageType) {
        this.messageType = messageType;
    }

    private void setText(String t) {
        this.text = t;
    }

    private void setFromUserId(Long f) {
        this.fromUserId = f;
    }

    private void setFromUserKey(String s) {
        this.fromUserKey = s;
    }

    private void setToUserId(Long u) {
        this.toUserId = u;
    }

}
